package org.lookahead.parent.common.mvc;

import org.lookahead.parent.apidoc.result.IResultCode;
import org.lookahead.parent.apidoc.result.R;
import org.lookahead.parent.apidoc.result.ResultCode;
import org.lookahead.parent.apidoc.result.ResultException;
import org.lookahead.parent.common.exceptions.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.net.SocketException;
import java.util.List;
import java.util.Objects;

/**
 * 统一异常处理
 * 重点处理参数校验的两个exception
 */
@ControllerAdvice
public class ControllerExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(ControllerExceptionHandler.class);

    /**
     * 处理exception
     *
     * @param ex 异常
     * @return ResultVo
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public R<String> exceptionHandler(Exception ex, HttpServletRequest request) {
        log.error("捕获到Exception异常", ex);
        return returnMsg(request, ResultCode.INTERNAL_SERVER_ERROR, null);
    }

    /**
     * 处理ResultException
     *
     * @param ex 异常
     * @return R
     */
    @ResponseBody
    @ExceptionHandler(value = ResultException.class)
    public R<String> resultExceptionHandler(ResultException ex, HttpServletRequest request) {
        log.error("捕获到ResultException异常", ex);
        return returnMsg(request, ResultCode.INTERNAL_SERVER_ERROR, ex.getMessage());
    }

    /**
     * 处理ResultException
     *
     * @param ex 异常
     * @return R
     */
    @ResponseBody
    @ExceptionHandler(value = ServiceException.class)
    public R<String> serviceExceptionHandler(ServiceException ex, HttpServletRequest request) {
        log.error("捕获到ServiceException异常", ex);
        return returnMsg(request, ex.getResultCode(), ex.getMessage());
    }

    /**
     * 处理 IllegalArgumentException
     *
     * @param ex 异常
     * @return R
     */
    @ResponseBody
    @ExceptionHandler(value = IllegalArgumentException.class)
    public R<String> illegalArgumentExceptionHandler(IllegalArgumentException ex, HttpServletRequest request) {
        log.error("捕获到IllegalArgumentException异常", ex);
        return returnMsg(request, ResultCode.INTERNAL_SERVER_ERROR, ex.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public R<String> httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex, HttpServletRequest request) {
        log.error("捕获到HttpRequestMethodNotSupportedException异常", ex);
        return returnMsg(request, ResultCode.METHOD_NOT_SUPPORTED, ex.getMessage());
    }

    /**
     * 处理SocketException
     *
     * @param ex 异常
     * @return R
     */
    @ResponseBody
    @ExceptionHandler(value = SocketException.class)
    public R<String> socketExceptionHandler(SocketException ex) {
        log.error("捕获到SocketException异常", ex);
        return R.fail(ex.getMessage());
    }

    /**
     * HttpMessageNotReadableException
     *
     * @param ex      异常
     * @param request 请求
     * @return R
     */
    @ResponseBody
    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    public R<String> httpMessageNotReadableExceptionHandler(HttpMessageNotReadableException ex, HttpServletRequest request) {
        log.error("捕获到HttpMessageNotReadableException异常", ex);
        return returnMsg(request, ResultCode.INTERNAL_SERVER_ERROR, null);
    }

    /**
     * ServletRequestBindingException
     *
     * @param ex      异常
     * @param request 请求
     * @return R
     */
    @ResponseBody
    @ExceptionHandler(value = ServletRequestBindingException.class)
    public R<String> servletRequestBindingExceptionHandler(ServletRequestBindingException ex, HttpServletRequest request) {
        log.error("捕获到ServletRequestBindingException异常", ex);
        return returnMsg(request, ResultCode.PARAM_BIND_ERROR, null);
    }

    /**
     * 处理参数校验异常
     *
     * @param exception 异常
     * @param request   请求
     * @return R
     */
    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R<String> exceptionHandler(MethodArgumentNotValidException exception, HttpServletRequest request) {
        log.error("发生MethodArgumentNotValidException...", exception);
        BindingResult result = exception.getBindingResult();
        StringBuilder errorString = new StringBuilder();
        if (result.hasErrors()) {
            List<ObjectError> errors = result.getAllErrors();
            errors.forEach(p -> {
                FieldError fieldError = (FieldError) p;
                log.error("参数校验错误: dto entity [{}],field [{}],message [{}]",
                        fieldError.getObjectName(), fieldError.getField(), fieldError.getDefaultMessage());
                errorString.append(fieldError.getDefaultMessage());
            });
        }
        return returnMsg(request, ResultCode.MSG_NOT_READABLE, errorString.toString());
    }

    private R<String> returnMsg(HttpServletRequest request, IResultCode resultCode, String errorMsg) {
        log.error("接口异常！接口:{},异常信息:{}", request.getServletPath(), errorMsg);
        if (Objects.nonNull(errorMsg)) {
            return R.fail(resultCode, errorMsg);
        }
        return R.fail(resultCode);
    }
}
